﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml.Linq;
using Amib.Threading;
using HPPUtil;

namespace HPPClientLibrary
{
    using DownLoad;

    public abstract class ClientBase : PropertyChangedBase, IClient
    {
        #region Fields

        protected int _csServerListenPort;

        protected HashSet<string> _knownDict;
        //protected Dictionary<string, string> _hashFullNameDict;

        protected Dictionary<string, HppFileInfo> _hashFileInfoDict;
        protected HashSet<string> _shareDir;

        protected SmartThreadPool _threadPool;
        protected DownloadManager _manager;

        protected List<SearchResult> _searchResult;

        #endregion

        #region Constructor

        public ClientBase()
        {
            _knownDict = new HashSet<string>();
            //_hashFullNameDict = new Dictionary<string, string>();

            _hashFileInfoDict = new Dictionary<string, HppFileInfo>();

            _shareDir = new HashSet<string>();

            _threadPool = new SmartThreadPool();
        }

        #endregion

        #region Implementation of IClient

        /// <summary>
        /// 客户端服务器监听端口
        /// </summary>
        public virtual int CSServerListenPort
        {
            get { return _csServerListenPort; }
            protected  set { _csServerListenPort = value; }
        }

        /// <summary>
        /// 正在连接或已经连接的中央服务器EndPoint
        /// </summary>
        public IPEndPoint ConnectServerEndPoint { get; protected set; }

        /// <summary>
        /// 线程池
        /// </summary>
        public virtual SmartThreadPool ThreadPool
        {
            get { return _threadPool; }
        }

        /// <summary>
        /// 添加到已Hash列表
        /// </summary>
        /// <param name="hash">被添加的列表</param>
        protected virtual void AddKnownDict(string hash)
        {
            lock (_knownDict)
            {
                if (!_knownDict.Contains(hash))
                {
                    _knownDict.Add(hash);
                    this.NotifyPropertyChanged(o => o.ReadOnlyKnownDict);
                   //OnDictChanged(this, new EventArgs());
                }
            }
        }
        
        /// <summary>
        /// 添加到已Hash列表
        /// </summary>
        /// <param name="addList">被添加的列表</param>
        protected virtual void AddKnownDict(IEnumerable<string> addList)
        {
            lock (_knownDict)
            {
                foreach (string s in addList)
                {
                    if (!_knownDict.Contains(s))
                    {
                        _knownDict.Add(s);
                    }
                }
                this.NotifyPropertyChanged(o => o.ReadOnlyKnownDict);
                //OnDictChanged(this, new EventArgs());
            }
        }

        /// <summary>
        /// 添加到Hash-文件信息对应列表
        /// </summary>
        /// <param name="pair">被添加的列表</param>
        protected virtual void AddHashFileInfoDict(KeyValuePair<string, HppFileInfo> pair)
        {
            lock (_hashFileInfoDict)
            {
                if (!_hashFileInfoDict.ContainsKey(pair.Key))
                {
                    _hashFileInfoDict.Add(pair.Key, pair.Value);
                    this.NotifyPropertyChanged(o => o.ReadOnlyHashFileInfoDict);
                    //OnDictChanged(this, new EventArgs());
                }
            }
        }

        /// <summary>
        /// 添加到Hash-文件信息对应列表
        /// </summary>
        /// <param name="addList">被添加的列表</param>
        protected virtual void AddHashFileInfoDict(IEnumerable<KeyValuePair<string, HppFileInfo>> addList)
        {
            lock (_hashFileInfoDict)
            {
                foreach (KeyValuePair<string, HppFileInfo> list in addList)
                {
                    if (!_hashFileInfoDict.ContainsKey(list.Key))
                    {
                        _hashFileInfoDict.Add(list.Key, list.Value);
                    }
                }
                this.NotifyPropertyChanged(o => o.ReadOnlyHashFileInfoDict);
                //OnDictChanged(this, new EventArgs());
            }
        }

        /*
        /// <summary>
        /// 添加到Hash-全路径对应列表
        /// </summary>
        /// <param name="pair">被添加的列表</param>
        protected virtual void AddHashFullNameDict(KeyValuePair<string, string> pair)
        {
            lock (_hashFullNameDict)
            {
                if (!_hashFullNameDict.ContainsKey(pair.Key))
                {
                    _hashFullNameDict.Add(pair.Key, pair.Value);
                    this.NotifyPropertyChanged(o => o.ReadOnlyHashFullNameDict);
                    //OnDictChanged(this, new EventArgs());
                }
            }
        }
        
        /// <summary>
        /// 添加到Hash-全路径对应列表
        /// </summary>
        /// <param name="addList">被添加的列表</param>
        protected virtual void AddHashFullNameDict(IEnumerable<KeyValuePair<string, string>> addList)
        {
            lock (_hashFullNameDict)
            {
                foreach (KeyValuePair<string, string> list in addList)
                {
                    if (!_hashFullNameDict.ContainsKey(list.Key))
                    {
                        _hashFullNameDict.Add(list.Key, list.Value);
                    }
                }
                this.NotifyPropertyChanged(o => o.ReadOnlyHashFullNameDict);
                //OnDictChanged(this, new EventArgs());
            }
        }
        */

        /// <summary>
        /// 添加到共享目录列表
        /// </summary>
        /// <param name="dir">被添加的列表</param>
        public virtual void AddShareDir(string dir)
        {
            lock (_shareDir)
            {
                string trimDir = dir.EndsWith("/") ? dir.Substring(0, dir.Length - 1) : dir;

                if (!_shareDir.Contains(trimDir))
                {
                    _shareDir.Add(trimDir);
                    this.NotifyPropertyChanged(o => o.ReadOnlyShareDir);
                }
            }
        }

        /// <summary>
        /// 添加到共享目录列表
        /// </summary>
        /// <param name="addList">被添加的列表</param>
        public virtual void AddShareDir(IEnumerable<string> addList)
        {
            lock (_shareDir)
            {
                foreach (string s in addList)
                {
                    string trimDir = s.EndsWith("/") ? s.Substring(0, s.Length - 1) : s;

                    if (!_shareDir.Contains(trimDir))
                    {
                        _shareDir.Add(trimDir);
                    }
                }
                //OnDictChanged(this, new EventArgs());
                this.NotifyPropertyChanged(i => i.ReadOnlyShareDir);
            }
        }

        /// <summary>
        /// 返回只读的共享目录列表
        /// </summary>
        /// <returns>只读的列表</returns>
        public virtual IEnumerable<string> GetShareDirAsReadOnly()
        {
            return _shareDir.AsEnumerable();
        }

        public abstract void Download(string hash, string fileName, bool isStartNow);


        /// <summary>
        /// 客户端服务器开始监听
        /// </summary>
        public abstract void StartListen();

        /// <summary>
        /// 连接到中央服务器
        /// </summary>
        /// <param name="ipEndPoint"></param>
        public abstract void ConnectServer(IPEndPoint ipEndPoint);

        /// <summary>
        /// 是否已连接到中央服务器
        /// </summary>
        public bool Connected { get; protected set; }

        /// <summary>
        /// 上传共享目录
        /// </summary>
        public abstract void UploadShareDir(IPEndPoint ipEndPoint);

        /// <summary>
        /// 向中央服务器搜索
        /// </summary>
        /// <param name="filename">搜索关键字</param>
        public virtual void Search(string filename)
        {
            ThreadPool.QueueWorkItem(new Action<string>(SearchInternal), filename);
        }

        protected void SearchInternal(string filename)
        {
            if (ConnectServerEndPoint == null)
            {
                CommonLogger.Logger.Warn("SearchInternal() ConnectServerEndPoint is null");
                return;
            }
            var uri = new Uri(string.Format("http://{0}:{1}/{2}|none/", ConnectServerEndPoint.Address, ConnectServerEndPoint.Port, filename));
            var request = (HttpWebRequest)HttpWebRequest.Create(uri);
            request.Method = "GET";
            using (var response = (HttpWebResponse)request.GetResponse())
            {
                string responseString;
                switch (response.StatusCode)
                {
                    case HttpStatusCode.NotFound:
                    case HttpStatusCode.Forbidden:

                        break;
                    case HttpStatusCode.OK:
                        using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                        {
                            responseString = streamReader.ReadToEnd().Trim();
                        }
                        XDocument doc;
                        try
                        {
                            doc = XDocument.Parse(responseString);
                        }
                        catch (Exception ex)
                        {
                            return;

                        }
                        if (doc.Descendants("File").Count() == 0)
                        {
                            return;
                        }
                        var users =
                            from u in doc.Descendants("File")
                            select
                                new
                                {
                                    filename = u.Attribute("FileName").Value,
                                    hash = u.Attribute("Hash").Value
                                };
                        List<SearchResult> results = new List<SearchResult>();
                        foreach (var user in users)
                        {
                            results.Add(new SearchResult()
                            {
                                FileName = user.filename,
                                Hash = user.hash
                            });
                        }

                        this.SearchResult = results;

                        this.NotifyPropertyChanged(o => o.SearchResult);

                        break;
                    default:
                        break;
                }
            }

            
        }
        
        public List<SearchResult> SearchResult
        {
            get
            {
                return _searchResult;
            }
            protected set
            {
                _searchResult = value;
            }
        }

        /// <summary>
        /// 下载管理器
        /// </summary>
        public abstract DownloadManager DownloadManager{ get; }

        public abstract ConfigManager Config { get; }

        #endregion
        

        #region Properties

        public abstract IHashCalculator HashCalculator { get; set; }

        public IEnumerable<string> ReadOnlyShareDir
        {
            get
            {
                return _shareDir.AsEnumerable();
            }
        }

        public IEnumerable<string> ReadOnlyKnownDict
        {
            get
            {
                return _knownDict.AsEnumerable();
            }
        }
/*
        public ReadOnlyDictionary<string, string> ReadOnlyHashFullNameDict
        {
            get
            {
                return new ReadOnlyDictionary<string, string>(this._hashFullNameDict);
            }
        }
        */
        public ReadOnlyDictionary<string, HppFileInfo> ReadOnlyHashFileInfoDict
        {
            get
            {
                return new ReadOnlyDictionary<string, HppFileInfo>(this._hashFileInfoDict);
            }
        }

        #endregion
    }
}
